package org.kabeja.dxf.entity;

import org.kabeja.dxf.DXFConstants;
import org.kabeja.dxf.helpers.Point;
import org.kabeja.dxf.helpers.Vector;
import org.kabeja.math.MathUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author shutao
 * @since 2022/4/25 9:09
 */
public class DXFPolyline extends DXFEntity {
    protected static final double QUARTER_CIRCLE_ANGLE = Math.tan(0.39269908169872414D);
    protected List<DXFVertex> vertices = new ArrayList<>();
    protected double startWidth = 0.0D;
    protected double endWidth = 0.0D;
    protected boolean constantWidth = true;
    protected int surefaceType = 0;
    protected int surefaceDensityRows = 0;
    protected int surefaceDensityColumns = 0;
    protected int rows = 0;
    protected int columns = 0;

    public DXFPolyline() {
        this.setLineType(this.getType());
    }

    @Override
    public Bounds getBounds() {
        Bounds var1 = new Bounds();
        Iterator<DXFVertex> var2 = this.vertices.iterator();
        if (var2.hasNext()) {
            DXFVertex var5 = null;
            DXFVertex var4;
            DXFVertex var3 = var4 = var2.next();
            var1.addToBounds(var3.getPoint());

            while(var2.hasNext()) {
                var5 = var2.next();
                this.addToBounds(var3, var5, var1);
                var3 = var5;
            }

            if (var5 != null && var5.getBulge() != 0.0D) {
                this.addToBounds(var5, var4, var1);
            }
        } else {
            var1.setValid(false);
        }

        return var1;
    }

    public void addVertex(DXFVertex var1) {
        this.vertices.add(var1);
        if (!var1.isConstantWidth()) {
            this.constantWidth = false;
        }

    }

    public int getVertexCount() {
        return this.vertices.size();
    }

    public Iterator<DXFVertex> getVertexIterator() {
        return this.vertices.iterator();
    }

    public void removeVertex(DXFVertex var1) {
        this.constantWidth = true;
        Iterator<DXFVertex> var2 = this.vertices.iterator();

        while(var2.hasNext()) {
            DXFVertex var3 = (DXFVertex)var2.next();
            if (var3 == var1) {
                var2.remove();
            } else if (!var3.isConstantWidth()) {
                this.constantWidth = false;
            }
        }

    }

    public void removeVertex(int var1) {
        this.constantWidth = true;

        for(int var2 = 0; var2 < this.vertices.size(); ++var2) {
            DXFVertex var3 = (DXFVertex)this.vertices.get(var2);
            if (var1 == var2) {
                this.vertices.remove(var2);
            } else if (!var3.isConstantWidth()) {
                this.constantWidth = false;
            }
        }

    }

    public DXFVertex getVertex(int var1) {
        return (DXFVertex)this.vertices.get(var1);
    }

    protected double getLength(DXFPoint var1, DXFPoint var2) {
        return Math.sqrt(Math.pow(var2.getX() - var1.getX(), 2.0D) + Math.pow(var2.getY() - var1.getY(), 2.0D));
    }

    public double getRadius(double var1, double var3) {
        double var5 = var1 * var3 / 2.0D;
        double var7 = var5 / 2.0D + Math.pow(var3, 2.0D) / (8.0D * var5);
        return Math.abs(var7);
    }

    @Override
    public String getType() {
        return DXFConstants.ENTITY_TYPE_POLYLINE;
    }

    public double getEndWidth() {
        return this.endWidth;
    }

    public void setEndWidth(double var1) {
        this.endWidth = var1;
    }

    public double getStartWidth() {
        return this.startWidth;
    }

    public void setStartWidth(double var1) {
        this.startWidth = var1;
    }

    public boolean isClosed() {
        return (this.flags & 1) == 1;
    }

    public boolean isCurveFitVerticesAdded() {
        return (this.flags & 2) == 2;
    }

    public boolean isSplineFitVerticesAdded() {
        return (this.flags & 4) == 4;
    }

    public boolean is3DPolygon() {
        return (this.flags & 8) == 8;
    }

    public boolean is3DPolygonMesh() {
        return (this.flags & 16) == 16;
    }

    public boolean isPolyfaceMesh() {
        return (this.flags & 64) == 64;
    }

    public boolean isClosedMeshNDirection() {
        return (this.flags & 32) == 32;
    }

    public boolean isClosedMeshMDirection() {
        return (this.flags & 1) == 1;
    }

    public boolean isQuadSpline() {
        if (this.isSplineFitVerticesAdded()) {
            return this.surefaceType == 5;
        } else {
            return false;
        }
    }

    public boolean isCubicSpline() {
        if (this.isSplineFitVerticesAdded()) {
            return this.surefaceType == 6;
        } else {
            return false;
        }
    }

    public boolean isConstantWidth() {
        if (!this.constantWidth) {
            return false;
        } else {
            this.constantWidth = true;
            Iterator<DXFVertex> var1 = this.vertices.iterator();

            DXFVertex var2;
            do {
                if (!var1.hasNext()) {
                    return this.constantWidth;
                }

                var2 = var1.next();
            } while(var2.isConstantWidth());

            this.constantWidth = false;
            return this.constantWidth;
        }
    }

    public int getSurefaceType() {
        return this.surefaceType;
    }

    public void setSurefaceType(int var1) {
        this.surefaceType = var1;
    }

    public int getSurefaceDensityColumns() {
        return this.surefaceDensityColumns;
    }

    public void setSurefaceDensityColumns(int var1) {
        this.surefaceDensityColumns = var1;
    }

    public int getSurefaceDensityRows() {
        return this.surefaceDensityRows;
    }

    public void setSurefaceDensityRows(int var1) {
        this.surefaceDensityRows = var1;
    }

    protected void addToBounds(DXFVertex var1, DXFVertex var2, Bounds var3) {
        if (var1.getBulge() != 0.0D) {
            double var4 = MathUtils.distance(var1.getPoint(), var2.getPoint());
            double var6 = this.getRadius(var1.getBulge(), var4);
            double var8 = var4 / 2.0D;
            Vector var10 = MathUtils.getVector(var1.getPoint(), var2.getPoint());
            var10 = MathUtils.normalize(var10);
            Point var11 = MathUtils.getPointOfStraightLine(var1.getPoint(), var10, var8);
            Vector var12 = MathUtils.crossProduct(var10, this.getExtrusion().getNormal());
            var12 = MathUtils.normalize(var12);
            double var13 = Math.abs(var1.getBulge() * var4) / 2.0D;
            boolean var15 = false;
            boolean var16 = false;
            double var17 = var1.getBulge();
            double var19;
            int var21;
            int var22;
            if (var17 > 0.0D) {
                if (var17 > 1.0D) {
                    var19 = var13 - var6;
                    var11 = MathUtils.getPointOfStraightLine(var11, var12, var19);
                } else {
                    var19 = var6 - var13;
                    var11 = MathUtils.getPointOfStraightLine(var11, var12, -1.0D * var19);
                }

                var22 = MathUtils.getQuadrant(var2.getPoint(), var11);
                var21 = MathUtils.getQuadrant(var1.getPoint(), var11);
            } else {
                if (var17 < -1.0D) {
                    var19 = var13 - var6;
                    var11 = MathUtils.getPointOfStraightLine(var11, var12, -1.0D * var19);
                } else {
                    var19 = var6 - var13;
                    var11 = MathUtils.getPointOfStraightLine(var11, var12, var19);
                }

                var21 = MathUtils.getQuadrant(var2.getPoint(), var11);
                var22 = MathUtils.getQuadrant(var1.getPoint(), var11);
            }

            if (var22 < var21) {
                var22 += 4;
            } else if (var22 == var21 && Math.abs(var1.getBulge()) > QUARTER_CIRCLE_ANGLE) {
                var22 += 4;
            }

            for(; var22 > var21; ++var21) {
                switch(var21) {
                    case 0:
                        var3.addToBounds(var11.getX(), var11.getY() + var6, var11.getZ());
                        break;
                    case 1:
                        var3.addToBounds(var11.getX() - var6, var11.getY(), var11.getZ());
                        break;
                    case 2:
                        var3.addToBounds(var11.getX(), var11.getY() - var6, var11.getZ());
                        break;
                    case 3:
                        var3.addToBounds(var11.getX() + var6, var11.getY(), var11.getZ());
                        var22 -= 4;
                        var21 -= 4;
                }
            }
        }

        var3.addToBounds(var1.getPoint());
        var3.addToBounds(var2.getPoint());
    }

    public DXFVertex getPolyFaceMeshVertex(int var1) {
        Iterator<DXFVertex> var2 = this.vertices.iterator();
        int var3 = 1;

        while(var2.hasNext()) {
            DXFVertex var4 = var2.next();
            if (var4.isPolyFaceMeshVertex()) {
                if (var3 == var1) {
                    return var4;
                }

                ++var3;
            }
        }

        return null;
    }

    public int getColumns() {
        return this.columns;
    }

    public void setColumns(int var1) {
        this.columns = var1;
    }

    public int getRows() {
        return this.rows;
    }

    public void setRows(int var1) {
        this.rows = var1;
    }

    public boolean isSimpleMesh() {
        return this.surefaceType == 0 && (this.flags & 4) == 0;
    }

    public boolean isQuadSurefaceMesh() {
        return this.surefaceType == 5 && (this.flags & 4) == 4;
    }

    public boolean isCubicSurefaceMesh() {
        return this.surefaceType == 6 && (this.flags & 4) == 4;
    }

    public boolean isBezierSurefaceMesh() {
        return this.surefaceType == 8 && (this.flags & 4) == 4;
    }

    @Override
    public double getLength() {
        double var1 = 0.0D;
        if (!this.isCubicSpline() && !this.isQuadSpline()) {
            if (this.isPolyfaceMesh()) {
                return this.getPolyfaceLength();
            } else if (!this.is3DPolygonMesh() && !this.isBezierSurefaceMesh() && !this.isCubicSurefaceMesh()) {
                Iterator<DXFVertex> var3 = this.vertices.iterator();

                DXFVertex var4;
                DXFVertex var5;
                DXFVertex var6;
                for(var5 = var4 = var3.next(); var3.hasNext(); var5 = var6) {
                    var6 = var3.next();
                    var1 += this.getSegmentLength(var5, var6);
                }

                if (this.isClosed()) {
                    var1 += this.getSegmentLength(var5, var4);
                }

                return var1;
            } else {
                return this.getMeshLength();
            }
        } else {
            return this.getSplineApproximationLength();
        }
    }

    protected double getSegmentLength(DXFVertex var1, DXFVertex var2) {
        double var3 = MathUtils.distance(var1.getPoint(), var2.getPoint());
        if (var1.getBulge() == 0.0D) {
            return var3;
        } else {
            double var5 = 4.0D * Math.atan(Math.abs(var1.getBulge()));
            double var7 = var3 / (2.0D * Math.sin(var5 / 2.0D));
            return Math.PI * Math.toDegrees(var5) * var7 / 180.0D;
        }
    }

    protected double getSplineApproximationLength() {
        double var1 = 0.0D;
        Iterator<DXFVertex> var3 = this.vertices.iterator();
        DXFVertex var4 = null;
        DXFVertex var5 = null;

        while(var3.hasNext()) {
            DXFVertex var6 = var3.next();
            if (var6.is2DSplineApproximationVertex()) {
                if (var4 == null) {
                    var5 = var6;
                    var4 = var6;
                } else {
                    var1 += this.getSegmentLength(var5, var6);
                    var5 = var6;
                }
            }
        }

        if (this.isClosed()) {
            var1 += this.getSegmentLength(var5, var4);
        }

        return var1;
    }

    protected double getPolyfaceLength() {
        double var1 = 0.0D;
        Iterator<DXFVertex> var3 = this.vertices.iterator();

        while(true) {
            while(true) {
                DXFVertex var4;
                do {
                    if (!var3.hasNext()) {
                        return var1;
                    }

                    var4 = var3.next();
                } while(!var4.isFaceRecord());

                DXFVertex var5 = this.getPolyFaceMeshVertex(var4.getPolyFaceMeshVertex0());
                DXFVertex var6 = this.getPolyFaceMeshVertex(var4.getPolyFaceMeshVertex1());
                DXFVertex var7 = this.getPolyFaceMeshVertex(var4.getPolyFaceMeshVertex2());
                DXFVertex var8 = this.getPolyFaceMeshVertex(var4.getPolyFaceMeshVertex3());
                if (var4.isPolyFaceEdge0Visible() && var4.getPolyFaceMeshVertex0() != 0) {
                    var1 += this.getSegmentLength(var5, var6);
                }

                if (var4.isPolyFaceEdge1Visible() && var4.getPolyFaceMeshVertex1() != 0) {
                    var1 += this.getSegmentLength(var6, var7);
                }

                if (var4.isPolyFaceEdge2Visible() && var4.getPolyFaceMeshVertex2() != 0) {
                    var1 += this.getSegmentLength(var7, var8);
                }

                if (var4.isPolyFaceEdge3Visible() && var4.getPolyFaceMeshVertex3() != 0) {
                    var1 += this.getSegmentLength(var8, var5);
                } else if (var8 == null && var7 != null) {
                    var1 += this.getSegmentLength(var7, var5);
                }
            }
        }
    }

    protected double getMeshLength() {
        double var1 = 0.0D;
        DXFVertex[][] var3;
        Iterator<DXFVertex> var4;
        if (this.isSimpleMesh()) {
            var3 = new DXFVertex[this.rows][this.columns];
            var4 = this.vertices.iterator();

            int var5;
            int var6;
            for(var5 = 0; var5 < this.rows; ++var5) {
                for(var6 = 0; var6 < this.columns; ++var6) {
                    DXFVertex var7 = var4.next();
                    var3[var5][var6] = var7;
                    if (var6 > 0) {
                        var1 += this.getSegmentLength(var3[var5][var6 - 1], var3[var5][var6]);
                    }
                }

                if (this.isClosedMeshNDirection()) {
                    var1 += this.getSegmentLength(var3[var5][var3[var5].length - 1], var3[var5][0]);
                }
            }

            for(var5 = 0; var5 < this.columns; ++var5) {
                for(var6 = 0; var6 < this.rows; ++var6) {
                    if (var6 > 0) {
                        var1 += this.getSegmentLength(var3[var6 - 1][var5], var3[var6][var5]);
                    }
                }

                if (this.isClosedMeshMDirection()) {
                    var1 += this.getSegmentLength(var3[var3[var5].length - 1][var5], var3[0][var5]);
                }
            }
        } else {
            var3 = new DXFVertex[this.surefaceDensityRows][this.surefaceDensityColumns];
            var4 = this.vertices.iterator();
            List<DXFVertex> var10 = new ArrayList<>();

            while(var4.hasNext()) {
                DXFVertex var12 = var4.next();
                if (var12.isMeshApproximationVertex()) {
                    var10.add(var12);
                }
            }

            Iterator<DXFVertex> var13 = var10.iterator();

            int var8;
            int var11;
            for(var11 = 0; var11 < this.surefaceDensityRows; ++var11) {
                for(var8 = 0; var8 < this.surefaceDensityColumns; ++var8) {
                    DXFVertex var9 = var13.next();
                    var3[var11][var8] = var9;
                    if (var8 > 0) {
                        var1 += this.getSegmentLength(var3[var11][var8 - 1], var3[var11][var8]);
                    }
                }

                if (this.isClosedMeshNDirection()) {
                    var1 += this.getSegmentLength(var3[var11][var3[var11].length - 1], var3[var11][0]);
                }
            }

            for(var11 = 0; var11 < this.surefaceDensityColumns; ++var11) {
                for(var8 = 0; var8 < this.surefaceDensityRows; ++var8) {
                    if (var8 > 0) {
                        var1 += this.getSegmentLength(var3[var8 - 1][var11], var3[var8][var11]);
                    }
                }

                if (this.isClosedMeshMDirection()) {
                    var1 += this.getSegmentLength(var3[var3[var11].length - 1][var11], var3[0][var11]);
                }
            }
        }

        return var1;
    }
}
